{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 创建函数工具\n",
    "## 工具装饰器@tool"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "938baa22046aea4f"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name='multiply' description='Multiply two numbers.' args_schema=<class 'langchain_core.utils.pydantic.multiply'> func=<function multiply at 0x10c21c680>\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "# Let's inspect some of the attributes associated with the tool.\n",
    "print(multiply)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:15:21.599642Z",
     "start_time": "2024-10-31T02:15:21.578110Z"
    }
   },
   "id": "c0276f9ad97d9553",
   "execution_count": 18
  },
  {
   "cell_type": "markdown",
   "source": [
    "也可创建异步工具"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "1fb4d6ce1cebe1ba"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name='amultiply' description='Multiply two numbers.' args_schema=<class 'langchain_core.utils.pydantic.amultiply'> coroutine=<function amultiply at 0x10c21f920>\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "async def amultiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "print(amultiply)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:15:21.633675Z",
     "start_time": "2024-10-31T02:15:21.623922Z"
    }
   },
   "id": "6318c5d8e0bdde6a",
   "execution_count": 19
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 对工具类入参增加描述"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "7deeff7e291c31df"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'description': 'Multiply a by the maximum of b.',\n 'properties': {'a': {'description': 'scale factor',\n   'title': 'A',\n   'type': 'string'},\n  'b': {'description': 'list of ints over which to take maximum',\n   'items': {'type': 'integer'},\n   'title': 'B',\n   'type': 'array'}},\n 'required': ['a', 'b'],\n 'title': 'multiply_by_max',\n 'type': 'object'}"
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from typing import Annotated, List\n",
    "\n",
    "\n",
    "@tool\n",
    "def multiply_by_max(\n",
    "        a: Annotated[str, \"scale factor\"],\n",
    "        b: Annotated[List[int], \"list of ints over which to take maximum\"],\n",
    ") -> int:\n",
    "    \"\"\"Multiply a by the maximum of b.\"\"\"\n",
    "    return a * max(b)\n",
    "\n",
    "\n",
    "multiply_by_max.args_schema.schema()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:15:21.657813Z",
     "start_time": "2024-10-31T02:15:21.638520Z"
    }
   },
   "id": "8c6735e359101ed8",
   "execution_count": 20
  },
  {
   "cell_type": "markdown",
   "source": [
    "通过定义Json参数，可以更进一步描述工具参数"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "c35c75445ed0f8d9"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "multiplication-tool\n",
      "Multiply two numbers.\n",
      "{'a': {'description': 'first number', 'title': 'A', 'type': 'integer'}, 'b': {'description': 'second number', 'title': 'B', 'type': 'integer'}}\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "from pydantic import BaseModel, Field\n",
    "\n",
    "\n",
    "class CalculatorInput(BaseModel):\n",
    "    a: int = Field(description=\"first number\")\n",
    "    b: int = Field(description=\"second number\")\n",
    "\n",
    "\n",
    "@tool(\"multiplication-tool\", args_schema=CalculatorInput, return_direct=True)\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "# Let's inspect some of the attributes associated with the tool.\n",
    "print(multiply.name)\n",
    "print(multiply.description)\n",
    "print(multiply.args)\n",
    "print(multiply.return_direct)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:15:21.668509Z",
     "start_time": "2024-10-31T02:15:21.659838Z"
    }
   },
   "id": "e396bc87723ed76e",
   "execution_count": 21
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 文档字符解析描述参数\n",
    "`parse_docstring` 设置为true"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "c77cf0000af77e58"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'description': 'The foo.',\n 'properties': {'bar': {'description': 'The bar.',\n   'title': 'Bar',\n   'type': 'string'},\n  'baz': {'description': 'The baz.', 'title': 'Baz', 'type': 'integer'}},\n 'required': ['bar', 'baz'],\n 'title': 'foo',\n 'type': 'object'}"
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "@tool(parse_docstring=True)\n",
    "def foo(bar: str, baz: int) -> str:\n",
    "    \"\"\"The foo.\n",
    "\n",
    "    Args:\n",
    "        bar: The bar.\n",
    "        baz: The baz.\n",
    "    \"\"\"\n",
    "    return bar\n",
    "\n",
    "\n",
    "foo.args_schema.schema()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:15:21.683364Z",
     "start_time": "2024-10-31T02:15:21.670883Z"
    }
   },
   "id": "26aa7a8bab4f2903",
   "execution_count": 22
  },
  {
   "cell_type": "markdown",
   "source": [
    "## StructuredTool\n",
    "StructuredTool.from_function类方法比@tool装饰器提供了更多的可配置性"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "7e9b0fbd81fc59dc"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n",
      "10\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.tools import StructuredTool\n",
    "\n",
    "\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "async def amultiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "calculator = StructuredTool.from_function(func=multiply, coroutine=amultiply)\n",
    "\n",
    "print(calculator.invoke({\"a\": 2, \"b\": 3}))\n",
    "print(await calculator.ainvoke({\"a\": 2, \"b\": 5}))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:15:21.706706Z",
     "start_time": "2024-10-31T02:15:21.688431Z"
    }
   },
   "id": "5518b25f414d052",
   "execution_count": 23
  },
  {
   "cell_type": "markdown",
   "source": [
    "也可通过配置:"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "359eeb577b67fc7e"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n",
      "Calculator\n",
      "multiply numbers\n",
      "{'a': {'description': 'first number', 'title': 'A', 'type': 'integer'}, 'b': {'description': 'second number', 'title': 'B', 'type': 'integer'}}\n"
     ]
    }
   ],
   "source": [
    "class CalculatorInput(BaseModel):\n",
    "    a: int = Field(description=\"first number\")\n",
    "    b: int = Field(description=\"second number\")\n",
    "\n",
    "\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "calculator = StructuredTool.from_function(\n",
    "    func=multiply,\n",
    "    name=\"Calculator\",\n",
    "    description=\"multiply numbers\",\n",
    "    args_schema=CalculatorInput,\n",
    "    return_direct=True,\n",
    "    # coroutine= ... <- you can specify an async method if desired as well\n",
    ")\n",
    "\n",
    "print(calculator.invoke({\"a\": 2, \"b\": 3}))\n",
    "print(calculator.name)\n",
    "print(calculator.description)\n",
    "print(calculator.args)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:15:21.721085Z",
     "start_time": "2024-10-31T02:15:21.709588Z"
    }
   },
   "id": "b6c5a99103efcd13",
   "execution_count": 24
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 从Runnables创建工具\n",
    "可以使用as_tool方法将接受string或dict输入的LangChain Runnables转换为工具，该方法允许为参数指定名称、描述和其他模式信息。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "ce96628d7abcca04"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'answer_style': {'title': 'Answer Style', 'type': 'string'}}"
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "from langchain_openai import ChatOpenAI\n",
    "from dotenv import load_dotenv\n",
    "from langchain_core.language_models import GenericFakeChatModel\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [(\"human\", \"Hello. Please respond in the style of {answer_style}.\")]\n",
    ")\n",
    "\n",
    "# Placeholder LLM\n",
    "load_dotenv()\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    # 若没有配置环境变量，请用百炼API Key将下行替换为：api_key=\"sk-xxx\",\n",
    "    openai_api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "    model_name=\"qwen-max\",\n",
    "    temperature=0,\n",
    ")\n",
    "chain = prompt | llm | StrOutputParser()\n",
    "\n",
    "as_tool = chain.as_tool(\n",
    "    name=\"Style responder\", description=\"Description of when to use tool.\"\n",
    ")\n",
    "as_tool.args"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:15:21.852127Z",
     "start_time": "2024-10-31T02:15:21.725139Z"
    }
   },
   "id": "e1fa87424ebe29e6",
   "execution_count": 25
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Subclass BaseTool\n",
    "你可以通过从BaseTool继承来定义一个自定义工具。这提供了对工具定义的最大控制，但需要编写更多的代码。\n",
    "\n",
    "> 类似实现：QuerySQLDataBaseTool、SQLDatabaseToolkit"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "404bf6effdb8e83b"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "from typing import Optional, Type\n",
    "\n",
    "from langchain_core.callbacks import (\n",
    "    AsyncCallbackManagerForToolRun,\n",
    "    CallbackManagerForToolRun,\n",
    ")\n",
    "from langchain_core.tools import BaseTool\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "\n",
    "class CalculatorInput(BaseModel):\n",
    "    a: int = Field(description=\"first number\")\n",
    "    b: int = Field(description=\"second number\")\n",
    "\n",
    "\n",
    "# Note: It's important that every field has type hints. BaseTool is a\n",
    "# Pydantic class and not having type hints can lead to unexpected behavior.\n",
    "class CustomCalculatorTool(BaseTool):\n",
    "    name: str = \"Calculator\"\n",
    "    description: str = \"useful for when you need to answer questions about math\"\n",
    "    args_schema: Type[BaseModel] = CalculatorInput\n",
    "    return_direct: bool = True\n",
    "\n",
    "    def _run(\n",
    "            self, a: int, b: int, run_manager: Optional[CallbackManagerForToolRun] = None\n",
    "    ) -> str:\n",
    "        \"\"\"Use the tool.\"\"\"\n",
    "        return f\"{a * b}\"\n",
    "\n",
    "    async def _arun(\n",
    "            self,\n",
    "            a: int,\n",
    "            b: int,\n",
    "            run_manager: Optional[AsyncCallbackManagerForToolRun] = None,\n",
    "    ) -> str:\n",
    "        \"\"\"Use the tool asynchronously.\"\"\"\n",
    "        # If the calculation is cheap, you can just delegate to the sync implementation\n",
    "        # as shown below.\n",
    "        # If the sync calculation is expensive, you should delete the entire _arun method.\n",
    "        # LangChain will automatically provide a better implementation that will\n",
    "        # kick off the task in a thread to make sure it doesn't block other async code.\n",
    "        return self._run(a, b, run_manager=run_manager.get_sync())"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:17:41.899887Z",
     "start_time": "2024-10-31T02:17:41.886503Z"
    }
   },
   "id": "e6c7cf086149c05c",
   "execution_count": 28
  },
  {
   "cell_type": "markdown",
   "source": [
    "**API Reference:**[AsyncCallbackManagerForToolRun](https://python.langchain.com/api_reference/core/callbacks/langchain_core.callbacks.manager.AsyncCallbackManagerForToolRun.html) | [CallbackManagerForToolRun](https://python.langchain.com/api_reference/core/callbacks/langchain_core.callbacks.manager.CallbackManagerForToolRun.html) | [BaseTool](https://python.langchain.com/api_reference/core/tools/langchain_core.tools.base.BaseTool.html)"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "daeb2bb5ab68f3e2"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Calculator\n",
      "useful for when you need to answer questions about math\n",
      "{'a': {'description': 'first number', 'title': 'A', 'type': 'integer'}, 'b': {'description': 'second number', 'title': 'B', 'type': 'integer'}}\n",
      "True\n",
      "6\n",
      "6\n"
     ]
    }
   ],
   "source": [
    "multiply = CustomCalculatorTool()\n",
    "print(multiply.name)\n",
    "print(multiply.description)\n",
    "print(multiply.args)\n",
    "print(multiply.return_direct)\n",
    "\n",
    "print(multiply.invoke({\"a\": 2, \"b\": 3}))\n",
    "print(await multiply.ainvoke({\"a\": 2, \"b\": 3}))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:17:44.080505Z",
     "start_time": "2024-10-31T02:17:44.073447Z"
    }
   },
   "id": "e94f8a9c5727e1e6",
   "execution_count": 29
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 创建异步tools"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "eb7a526a466b4c3c"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n",
      "10\n"
     ]
    },
    {
     "ename": "NotImplementedError",
     "evalue": "StructuredTool does not support sync invocation.",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mNotImplementedError\u001B[0m                       Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[32], line 26\u001B[0m\n\u001B[1;32m     23\u001B[0m \u001B[38;5;250m    \u001B[39m\u001B[38;5;124;03m\"\"\"Multiply two numbers.\"\"\"\u001B[39;00m\n\u001B[1;32m     24\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m a \u001B[38;5;241m*\u001B[39m b\n\u001B[0;32m---> 26\u001B[0m multiply2\u001B[38;5;241m.\u001B[39minvoke({\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124ma\u001B[39m\u001B[38;5;124m\"\u001B[39m: \u001B[38;5;241m2\u001B[39m, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mb\u001B[39m\u001B[38;5;124m\"\u001B[39m: \u001B[38;5;241m3\u001B[39m})\n",
      "File \u001B[0;32m/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langchain_core/tools/base.py:484\u001B[0m, in \u001B[0;36mBaseTool.invoke\u001B[0;34m(self, input, config, **kwargs)\u001B[0m\n\u001B[1;32m    477\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21minvoke\u001B[39m(\n\u001B[1;32m    478\u001B[0m     \u001B[38;5;28mself\u001B[39m,\n\u001B[1;32m    479\u001B[0m     \u001B[38;5;28minput\u001B[39m: Union[\u001B[38;5;28mstr\u001B[39m, \u001B[38;5;28mdict\u001B[39m, ToolCall],\n\u001B[1;32m    480\u001B[0m     config: Optional[RunnableConfig] \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m,\n\u001B[1;32m    481\u001B[0m     \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs: Any,\n\u001B[1;32m    482\u001B[0m ) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m Any:\n\u001B[1;32m    483\u001B[0m     tool_input, kwargs \u001B[38;5;241m=\u001B[39m _prep_run_args(\u001B[38;5;28minput\u001B[39m, config, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs)\n\u001B[0;32m--> 484\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mrun(tool_input, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs)\n",
      "File \u001B[0;32m/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langchain_core/tools/base.py:689\u001B[0m, in \u001B[0;36mBaseTool.run\u001B[0;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, tool_call_id, **kwargs)\u001B[0m\n\u001B[1;32m    687\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m error_to_raise:\n\u001B[1;32m    688\u001B[0m     run_manager\u001B[38;5;241m.\u001B[39mon_tool_error(error_to_raise)\n\u001B[0;32m--> 689\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m error_to_raise\n\u001B[1;32m    690\u001B[0m output \u001B[38;5;241m=\u001B[39m _format_output(content, artifact, tool_call_id, \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mname, status)\n\u001B[1;32m    691\u001B[0m run_manager\u001B[38;5;241m.\u001B[39mon_tool_end(output, color\u001B[38;5;241m=\u001B[39mcolor, name\u001B[38;5;241m=\u001B[39m\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mname, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs)\n",
      "File \u001B[0;32m/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langchain_core/tools/base.py:657\u001B[0m, in \u001B[0;36mBaseTool.run\u001B[0;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, tool_call_id, **kwargs)\u001B[0m\n\u001B[1;32m    655\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m config_param \u001B[38;5;241m:=\u001B[39m _get_runnable_config_param(\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_run):\n\u001B[1;32m    656\u001B[0m     tool_kwargs[config_param] \u001B[38;5;241m=\u001B[39m config\n\u001B[0;32m--> 657\u001B[0m response \u001B[38;5;241m=\u001B[39m context\u001B[38;5;241m.\u001B[39mrun(\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_run, \u001B[38;5;241m*\u001B[39mtool_args, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mtool_kwargs)\n\u001B[1;32m    658\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mresponse_format \u001B[38;5;241m==\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mcontent_and_artifact\u001B[39m\u001B[38;5;124m\"\u001B[39m:\n\u001B[1;32m    659\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(response, \u001B[38;5;28mtuple\u001B[39m) \u001B[38;5;129;01mor\u001B[39;00m \u001B[38;5;28mlen\u001B[39m(response) \u001B[38;5;241m!=\u001B[39m \u001B[38;5;241m2\u001B[39m:\n",
      "File \u001B[0;32m/opt/anaconda3/envs/ai_312/lib/python3.12/site-packages/langchain_core/tools/structured.py:82\u001B[0m, in \u001B[0;36mStructuredTool._run\u001B[0;34m(self, config, run_manager, *args, **kwargs)\u001B[0m\n\u001B[1;32m     80\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mfunc(\u001B[38;5;241m*\u001B[39margs, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs)\n\u001B[1;32m     81\u001B[0m msg \u001B[38;5;241m=\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mStructuredTool does not support sync invocation.\u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m---> 82\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mNotImplementedError\u001B[39;00m(msg)\n",
      "\u001B[0;31mNotImplementedError\u001B[0m: StructuredTool does not support sync invocation."
     ]
    }
   ],
   "source": [
    "from langchain_core.tools import StructuredTool\n",
    "\n",
    "\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "async def amultiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "calculator = StructuredTool.from_function(func=multiply, coroutine=amultiply)\n",
    "\n",
    "print(calculator.invoke({\"a\": 2, \"b\": 3}))\n",
    "print(\n",
    "    await calculator.ainvoke({\"a\": 2, \"b\": 5})\n",
    ")  # Uses use provided amultiply without additional overhead\n",
    "\n",
    "\n",
    "@tool\n",
    "async def multiply2(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "multiply2.invoke({\"a\": 2, \"b\": 3})\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:28:44.120646Z",
     "start_time": "2024-10-31T02:28:43.423956Z"
    }
   },
   "id": "65a84959587303fc",
   "execution_count": 32
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Handling Tool Errors\n",
    "\n",
    "## 方式一，注册时配置异常描述"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "9a90e2a8f29f3f2d"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "\"There is no such city, but it's probably above 0K there!\""
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.tools import ToolException\n",
    "\n",
    "\n",
    "def get_weather(city: str) -> int:\n",
    "    \"\"\"Get weather for the given city.\"\"\"\n",
    "    raise ToolException(f\"Error: There is no city by the name of {city}.\")\n",
    "\n",
    "\n",
    "get_weather_tool = StructuredTool.from_function(\n",
    "    func=get_weather,\n",
    "    handle_tool_error=\"There is no such city, but it's probably above 0K there!\",\n",
    ")\n",
    "\n",
    "get_weather_tool.invoke({\"city\": \"foobar\"})\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:31:26.443157Z",
     "start_time": "2024-10-31T02:31:26.431692Z"
    }
   },
   "id": "62510fe580cfe182",
   "execution_count": 33
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 方式二 使用function"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "eb0ee8cdaf8d6e39"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "'The following errors occurred during tool execution: `Error: There is no city by the name of foobar.`'"
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def _handle_tool_error(error: Exception) -> str:\n",
    "    return f\"The following errors occurred during tool execution: `{error.args[0]}`\"\n",
    "\n",
    "\n",
    "get_weather_tool = StructuredTool.from_function(\n",
    "    func=get_weather,\n",
    "    handle_tool_error=_handle_tool_error,\n",
    ")\n",
    "get_weather_tool.invoke({\"city\": \"foobar\"})\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:33:20.727856Z",
     "start_time": "2024-10-31T02:33:20.715076Z"
    }
   },
   "id": "77bebabc9d654a1c",
   "execution_count": 34
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 返回工具执行的产物/结果/输出\n",
    "\n",
    "`langchain-core >= 0.2.19`"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "61cd0419e3161d37"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "'Successfully generated array of 10 random ints in [0, 9].'"
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import random\n",
    "from typing import List, Tuple\n",
    "\n",
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool(response_format=\"content_and_artifact\")\n",
    "def generate_random_ints(min: int, max: int, size: int) -> Tuple[str, List[int]]:\n",
    "    \"\"\"Generate size random ints in the range [min, max].\"\"\"\n",
    "    array = [random.randint(min, max) for _ in range(size)]\n",
    "    content = f\"Successfully generated array of {size} random ints in [{min}, {max}].\"\n",
    "    return content, array\n",
    "\n",
    "\n",
    "generate_random_ints.invoke({\"min\": 0, \"max\": 9, \"size\": 10})\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:34:51.484321Z",
     "start_time": "2024-10-31T02:34:51.472136Z"
    }
   },
   "id": "f6c98b13de5af79e",
   "execution_count": 36
  },
  {
   "cell_type": "markdown",
   "source": [
    "包含工具的内容及调用信息"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "9ea5ebe52efe1a8"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "ToolMessage(content='Successfully generated array of 10 random ints in [0, 9].', name='generate_random_ints', tool_call_id='123', artifact=[3, 5, 9, 5, 4, 8, 8, 7, 6, 4])"
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "generate_random_ints.invoke(\n",
    "    {\n",
    "        \"name\": \"generate_random_ints\",\n",
    "        \"args\": {\"min\": 0, \"max\": 9, \"size\": 10},\n",
    "        \"id\": \"123\",  # required\n",
    "        \"type\": \"tool_call\",  # required\n",
    "    }\n",
    ")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:35:19.339957Z",
     "start_time": "2024-10-31T02:35:19.332575Z"
    }
   },
   "id": "19f60a3756185286",
   "execution_count": 37
  },
  {
   "cell_type": "markdown",
   "source": [
    "We can do the same when subclassing BaseTool:\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "eef15b4eacc96734"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "ToolMessage(content='Generated 3 floats in [0.1, 3.3333], rounded to 4 decimals.', name='generate_random_floats', tool_call_id='123', artifact=[0.941, 1.1105, 1.443])"
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.tools import BaseTool\n",
    "\n",
    "\n",
    "class GenerateRandomFloats(BaseTool):\n",
    "    name: str = \"generate_random_floats\"\n",
    "    description: str = \"Generate size random floats in the range [min, max].\"\n",
    "    response_format: str = \"content_and_artifact\"\n",
    "\n",
    "    ndigits: int = 2\n",
    "\n",
    "    def _run(self, min: float, max: float, size: int) -> Tuple[str, List[float]]:\n",
    "        range_ = max - min\n",
    "        array = [\n",
    "            round(min + (range_ * random.random()), ndigits=self.ndigits)\n",
    "            for _ in range(size)\n",
    "        ]\n",
    "        content = f\"Generated {size} floats in [{min}, {max}], rounded to {self.ndigits} decimals.\"\n",
    "        return content, array\n",
    "\n",
    "    # Optionally define an equivalent async method\n",
    "\n",
    "    # async def _arun(self, min: float, max: float, size: int) -> Tuple[str, List[float]]:\n",
    "    #     ...\n",
    "\n",
    "\n",
    "rand_gen = GenerateRandomFloats(ndigits=4)\n",
    "\n",
    "rand_gen.invoke(\n",
    "    {\n",
    "        \"name\": \"generate_random_floats\",\n",
    "        \"args\": {\"min\": 0.1, \"max\": 3.3333, \"size\": 3},\n",
    "        \"id\": \"123\",\n",
    "        \"type\": \"tool_call\",\n",
    "    }\n",
    ")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-10-31T02:38:00.424670Z",
     "start_time": "2024-10-31T02:38:00.407717Z"
    }
   },
   "id": "4f710f0e8fb3a372",
   "execution_count": 38
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
